/*************************************************************************/
/*                                                                       */
/*  Copyright (c) 1994 Stanford University                               */
/*                                                                       */
/*  All rights reserved.                                                 */
/*                                                                       */
/*  Permission is given to use, copy, and modify this software for any   */
/*  non-commercial purpose as long as this copyright notice is not       */
/*  removed.  All other uses, including redistribution in whole or in    */
/*  part, are forbidden without prior written permission.                */
/*                                                                       */
/*  This software is provided with absolutely no warranty and no         */
/*  support.                                                             */
/*                                                                       */
/*************************************************************************/


/*
 * NAME
 *  husetup.c
 *
 * DESCRIPTION
 *  This file contains routines that initialize, build, traverse, and
 *  intersect the hierarchical uniform grid (HUG) representation of the scene.
 *
 */


#include <stdio.h>
#include <math.h>
#include "rt.h"
#include <pool_notify.h>


INT DSPisWorking = FALSE;
// set the minimum number of jobs to send to the DSP
#define JOBS_MIN_DSP    15
// set the maximum number of jobs to send to the DSP
#define JOBS_MAX_DSP    15
#define FUNC_TRIPEINTERSECT         1
#define FUNC_SPHPEINTERSECT         2
#define FUNC_POLYPEINTERSECT        3
#define FUNC_UNKNOWN_PEINTERSECT    4

#define CALLER_IntersectHuniformPrimlist    1

#define CLOCKWISE       1
#define COUNTER_CLOCKWISE   2
/*
 * NAME
 *  Huniform_defaults - setup the five HUG parameter defaults
 *
 * SYNOPSIS
 *  VOID    Huniform_defaults()
 *
 * RETURNS
 *  Nothing.
 */

VOID    Huniform_defaults()
{
    hu_max_prims_cell   = 2;
    hu_gridsize     = 5;
    hu_numbuckets       = 23;
    hu_max_subdiv_level = 1;
    hu_lazy         = 0;
}



/*
 * NAME
 *  BuildHierarchy_Uniform - build HU grid from model
 *
 * SYNOPSIS
 *  VOID    BuildHierarchy_Uniform()
 *
 * RETURNS
 *  Nothing.
 *
 */

VOID    BuildHierarchy_Uniform()
{
    INT num_pe;
    GRID    *g;
    GRID    *ng;
    VOXEL   *v;
    ELEMENT **pepa;

    init_masks();


    pepa = MakeElementArray(&num_pe);

    v = init_world_voxel(pepa,num_pe);

    gm->world_level_grid = init_world_grid(v, pepa, num_pe);
    g = gm->world_level_grid;

    ng = create_grid(v, g, num_pe);

    fprintf(stderr, "Uniform Hierarchy built.\n");
}




/*
 * NAME
 *  SendTasksToDSP - Send tasks to the dsp
 *
 * SYNOPSIS
 *  INT    SendTasksToDSP(RAY *pr, ELEMENT **pptr, INT curr, INT CallerCode, INT numIterations)
 *
 * RETURNS
 *  number of tasks send.
 *
 * the function copies the data from the right structs to the shared memory buffer.
 * at the end the function calls the pool_notify_Execute to validate the data and notify the DSP.
 * it also set's the value that the dsp is working and can not be bothered.
 *
 */
INT size = 0;
INT SendTasksToDSP(RAY *pr, ELEMENT **pptr, INT curr, INT CallerCode, INT numIterations){
    INT i=0, j;
    ELEMENT *pe;
    TRI *pt;
    SPHERE  *ps;
    POLY    *pp;
    VEC3    *v1, *v2, *v3;      /* Vertex list pointers.         */
    REAL* pool_notify_DataBuf = return_Bufferaddr();
    VEC3    *vpos;
    INT *vindex;
    
   // printf("SendTasksToDSP\n");
    pool_notify_DataBuf[0] = numIterations;
    pool_notify_DataBuf[1] = (REAL)CallerCode;
    pool_notify_DataBuf[2] = (REAL)curr;
    i=5; //Skip [3] location for total size.
    pool_notify_DataBuf[4] = pr->ri->t_out;    
    
    for(j=0;j<numIterations;j++){
        pe = pptr[curr+j];
        if(pe->parent->procs->pe_intersect == TriPeIntersect){
            pool_notify_DataBuf[i++] = FUNC_TRIPEINTERSECT;
            
            pt = (TRI *)pe->data;
            
            v1 = pt->vptr + pt->vindex[0];  /* Compute first vertex pointer.     */
            if (pt->vorder == COUNTER_CLOCKWISE)
            {
                v2 = pt->vptr + pt->vindex[2];
                v3 = pt->vptr + pt->vindex[1];
            }
            else
            {
                v2 = pt->vptr + pt->vindex[1];
                v3 = pt->vptr + pt->vindex[2];
            }
    
            pool_notify_DataBuf[i++] = pr->P[0];
            pool_notify_DataBuf[i++] = pr->P[1];
            pool_notify_DataBuf[i++] = pr->P[2];
            pool_notify_DataBuf[i++] = pr->D[0];
            pool_notify_DataBuf[i++] = pr->D[1];
            pool_notify_DataBuf[i++] = pr->D[2];
            pool_notify_DataBuf[i++] = pt->norm[0];
            pool_notify_DataBuf[i++] = pt->norm[1];
            pool_notify_DataBuf[i++] = pt->norm[2];
            
            pool_notify_DataBuf[i++] = (*v1)[0];
            pool_notify_DataBuf[i++] = (*v1)[1];
            pool_notify_DataBuf[i++] = (*v1)[2];
            pool_notify_DataBuf[i++] = (*v2)[0];
            pool_notify_DataBuf[i++] = (*v2)[1];
            pool_notify_DataBuf[i++] = (*v2)[2];
            pool_notify_DataBuf[i++] = (*v3)[0];
            pool_notify_DataBuf[i++] = (*v3)[1];
            pool_notify_DataBuf[i++] = (*v3)[2];
            
            pool_notify_DataBuf[i++] = pt->d;
            pool_notify_DataBuf[i++] = pt->indx;
            
        }else if(pe->parent->procs->pe_intersect==SphPeIntersect){
            pool_notify_DataBuf[i++] = FUNC_SPHPEINTERSECT;
            
            ps  = (SPHERE *)(pe->data);
    
            pool_notify_DataBuf[i++] = pr->P[0];
            pool_notify_DataBuf[i++] = pr->P[1];
            pool_notify_DataBuf[i++] = pr->P[2];
            pool_notify_DataBuf[i++] = pr->D[0];
            pool_notify_DataBuf[i++] = pr->D[1];
            pool_notify_DataBuf[i++] = pr->D[2];
            pool_notify_DataBuf[i++] = ps->center[0];
            pool_notify_DataBuf[i++] = ps->center[1];
            pool_notify_DataBuf[i++] = ps->center[2];
            pool_notify_DataBuf[i++] = ps->rad2;
            
        }else if(pe->parent->procs->pe_intersect==PolyPeIntersect){
            pool_notify_DataBuf[i++] = (CHAR)FUNC_POLYPEINTERSECT;
            
            pp = (POLY *)pe->data; 
            vindex = pp->vindex;
    
            pool_notify_DataBuf[i++] = pr->P[0];
            pool_notify_DataBuf[i++] = pr->P[1];
            pool_notify_DataBuf[i++] = pr->P[2];
            pool_notify_DataBuf[i++] = pr->D[0];
            pool_notify_DataBuf[i++] = pr->D[1];
            pool_notify_DataBuf[i++] = pr->D[2];
            pool_notify_DataBuf[i++] = pp->d;
            pool_notify_DataBuf[i++] = pp->norm[0];
            pool_notify_DataBuf[i++] = pp->norm[1];
            pool_notify_DataBuf[i++] = pp->norm[2];
            vpos = pp->vptr + (*vindex);
            pool_notify_DataBuf[i++] = (*vpos)[0];
            pool_notify_DataBuf[i++] = (*vpos)[1];
            pool_notify_DataBuf[i++] = (*vpos)[2];
            vindex++;
            vpos = pp->vptr + (*vindex);
            pool_notify_DataBuf[i++] = (*vpos)[0];
            pool_notify_DataBuf[i++] = (*vpos)[1];
            pool_notify_DataBuf[i++] = (*vpos)[2];
            vindex++;
            vpos = pp->vptr + (*vindex);
            pool_notify_DataBuf[i++] = (*vpos)[0];
            pool_notify_DataBuf[i++] = (*vpos)[1];
            pool_notify_DataBuf[i++] = (*vpos)[2];
            vindex++;
            vpos = pp->vptr + (*(pp->vindex));
            pool_notify_DataBuf[i++] = (*vpos)[0];
            pool_notify_DataBuf[i++] = (*vpos)[1];
            pool_notify_DataBuf[i++] = (*vpos)[2];            
            pool_notify_DataBuf[i++] = pp->axis_proj;
            pool_notify_DataBuf[i++] = pp->nverts;
        }else{
            pool_notify_DataBuf[i++] = FUNC_UNKNOWN_PEINTERSECT;
        }
    }
    pool_notify_DataBuf[3] = (INT)i;
    
    // run pool_notify_Execute with value 1 for contineous looping, 0 to make the DSP stop looping!
    pool_notify_Execute(1);
   

    
    DSPisWorking = TRUE;
    return j;
}


/*
 * NAME
 *  TryRetrieveResult - checks if the dsp has a result
 *
 * SYNOPSIS
 *  VOID TryRetrieveResult(ELEMENT **pptr, IRECORD *hit, REAL ComparingValue)
 * 
 * IF a notify is received the function will read out the data and check for every calculation 
 * If the hit.tval value is smaller than the previous one as in the original code.
 * 
 *
 * RETURNS
 *  Nothing.
 *
 */
VOID TryRetrieveResult(ELEMENT **pptr, IRECORD *hit, REAL ComparingValue){
    INT j;
    INT state = pool_notify_TryWait();
    
    REAL* pool_notify_DataBuf = return_Bufferaddr();
    
    if(state==0){
    
        for(j=0;j<pool_notify_DataBuf[0];j++){        
        
            // Data Arrangement
            INT CallerCode  = (INT)     pool_notify_DataBuf[1];
            INT SizeOfData  = (INT)     pool_notify_DataBuf[3];
            INT hitcode     = (INT)     pool_notify_DataBuf[ SizeOfData +(j*10) ];
            REAL newhit_t   = (REAL)    pool_notify_DataBuf[ SizeOfData+1 +(j*10)];
            INT pptr_offset = (INT)pool_notify_DataBuf[2] + (INT)pool_notify_DataBuf[ SizeOfData+2 +(j*10)];
            ELEMENT *pelem  = pptr[pptr_offset];
            REAL    b1      = (REAL)    pool_notify_DataBuf[ SizeOfData+3 +(j*10)];
            REAL    b2      = (REAL)    pool_notify_DataBuf[ SizeOfData+4 +(j*10)];         /* intersection for triangles.       */
            REAL    b3      = (REAL)    pool_notify_DataBuf[ SizeOfData+5 +(j*10)];
            
            
            switch(CallerCode){
                case CALLER_IntersectHuniformPrimlist:
                    if (hitcode)
                        if (newhit_t < hit[0].t && newhit_t < ComparingValue){
                            hit[0].t = newhit_t;
                            hit[0].pelem = pelem;
                            hit[0].b1 = b1;
                            hit[0].b2 = b2;
                            hit[0].b3 = b3;
                        }
                    break;
                default:
                    printf("Unknown CallerCode. Error and exit.\n");
                    exit(-1);
                    break;
            }
        }
        DSPisWorking = FALSE;
    }
}

/*
 * NAME
 *  IntersectHuniformPrimlist - intersect ray with objects in HU cell
 *
 * SYNOPSIS
 *  VOID    IntersectHuniformPrimlist(intersectPrim, hit, plist, r, pid)
 *  BOOL    *intersectPrim;     // Primitive hit?
 *  IRECORD *hit;           // Intersection record.
 *  ELEMENT **plist;        // Primitive element list.
 *  RAY *r;         // Original ray.
 *  INT pid;            // Process id.
 *
 * RETURNS
 *  Nothing.
 *
 */

VOID    IntersectHuniformPrimlist(INT *intersectPrim, IRECORD *hit, VOXEL *v, RAY *r, INT pid)
{
    ELEMENT **pptr;         /* Primitive element list ptr.       */
    OBJECT  *peParent;      /* Ptr to parent object.         */
    ELEMENT *pe;            /* Primitive element ptr.        */
    IRECORD newhit[ISECT_MAX];  /* Hit recorder.             */
    INT hitcode,i;
    REAL    t_out;
    INT RemainingCalls = v->numelements;
    INT CalculationToBeSent;
    INT status;

    t_out = r->ri->t_out;
    hit[0].t = HUGE_REAL;
    pptr = (ELEMENT**)v->cell;

    for (i = 0; i < v->numelements; i++)
    {
        RemainingCalls=(v->numelements-i);
        if( DSPisWorking==FALSE && RemainingCalls > (JOBS_MIN_DSP*6)){
            CalculationToBeSent = JOBS_MAX_DSP>(RemainingCalls>>1) ? (RemainingCalls>>1):  JOBS_MAX_DSP;
//printf("CalculationToBeSent = %d\n",CalculationToBeSent);            
            status = SendTasksToDSP(r, pptr, i, CALLER_IntersectHuniformPrimlist, CalculationToBeSent);
            if(status==CalculationToBeSent){
                i+=CalculationToBeSent;
            }else{
                printf("Error When Sending Calculation Task.");
            }
        }
        
        pe   = pptr[i];
        peParent = pe->parent;
        hitcode  = (*peParent->procs->pe_intersect)(r, pe, newhit);

        
        if (hitcode)
            if (newhit[0].t < hit[0].t && newhit[0].t < t_out)
                hit[0] = newhit[0];
        

        TryRetrieveResult(pptr, hit, t_out);

    }
    while(DSPisWorking==TRUE){
        TryRetrieveResult(pptr, hit, t_out);
        //printf(" WAITING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
    }

    if (hit[0].t < HUGE_REAL)
        *intersectPrim = TRUE;
    else
        *intersectPrim = FALSE;
}



/*
 * NAME
 *  HuniformShadowIntersect - intersect shadow ray with objects in HU cell
 *
 * SYNOPSIS
 *  REAL    HuniformShadowIntersect(r, lightlength, pe, pid)
 *  RAY *r;         // Incident ray.
 *  REAL    lightlength;        // Distance to light.
 *  ELEMENT *pe;            // Primitive element of shadow ray origin.
 *  INT pid;            // Process number.
 *
 * RETURNS
 *  Transparency value along ray to light.
 *
 */

REAL    HuniformShadowIntersect(RAY *r, REAL lightlength, ELEMENT *pe, INT pid)
{
    INT status;
    INT hitcode,i;
    INT RemainingCalls;
    REAL    trans;          /* Transparency factor.          */
    OBJECT  *peParent;      /* Ptr to parent object.         */
    ELEMENT **pptr;         /* Primitive element list ptr.       */
    ELEMENT *pe2;           /* Ptr to element.           */
    IRECORD newhit[ISECT_MAX];  /* Hit record.               */
    VOXEL   *v;

    trans = 1.0;

    /* Now try uniform hierarchy. */

    r->ri = NULL;
    v = init_ray(r, gm->world_level_grid);

    if (v == NULL)
    {
        reset_rayinfo(r);
        return(trans);
    }

    newhit[0].t = HUGE_REAL;
    status      = IN_WORLD;

    while (trans > 0.0 && status != EXITED_WORLD)
    {
        /* Intersect primitives in cell. */

        pptr = (ELEMENT**)v->cell;

        for (i = 0; (i < v->numelements) && (trans > 0.0); i++)
        {
            pe2  = pptr[i];
            peParent = pe2->parent;
            hitcode  = (*peParent->procs->pe_intersect)(r, pe2, newhit);

            if (hitcode && newhit[0].pelem != pe && newhit[0].t < lightlength)
                trans *= newhit[0].pelem->parent->surf->ktran;
        }

        if (trans > 0.0)
            v = next_nonempty_leaf(r, STEP, &status);
    }

    reset_rayinfo(r);
    return (trans);
}



/*
 * NAME
 *  TraverseHierarchyUniform - walk the HU grid to intersect a ray
 *
 * SYNOPSIS
 *  BOOL    TraverseHierarchyUniform(r, hit, pid)
 *  RAY *r;
 *  IRECORD *hit;
 *  INT pid;
 *
 * RETURNS
 *  TRUE if the ray hit something, FALSE otherwise.
 *
 */

BOOL    TraverseHierarchyUniform(RAY *r, IRECORD *hit, INT pid)
{
    INT status;
    INT intersectPrim;
    VOXEL   *v;

    r->ri = NULL;
    v = init_ray(r, gm->world_level_grid);

    if (v == NULL)
    {
        reset_rayinfo(r);
        return (FALSE);
    }

    intersectPrim = FALSE;
    hit[0].t      = HUGE_REAL;
    status        = IN_WORLD;

    while (!intersectPrim && status != EXITED_WORLD)
    {
        if(v->numelements!=0)
            IntersectHuniformPrimlist(&intersectPrim, hit, v, r, pid);
        else
            intersectPrim= FALSE;

        if (!intersectPrim)
            v = next_nonempty_leaf(r, STEP, &status);
    }

    reset_rayinfo(r);
    return (intersectPrim);
}

